package com.sqbr.video.service.impl;

import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.sqbr.video.core.exception.KunBusinessException;
import com.sqbr.video.core.exception.KunLoginException;
import com.sqbr.video.core.utlis.IdUtil;
import com.sqbr.video.core.utlis.PasswordUtil;
import com.sqbr.video.domain.entity.User;
import com.sqbr.video.domain.form.UserPwdLoginForm;
import com.sqbr.video.domain.form.UserRegisterForm;
import com.sqbr.video.mapper.UserMapper;
import com.sqbr.video.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.security.auth.login.LoginException;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Objects;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author author
 * @since 2024-11-06
 */
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    private final RedisTemplate redisTemplate;
    /**
     * 用户注册
     * desc：一个手机号只能注册1次 已在数据库设置唯一约束
     * 1.处理用户实体
     * 2.执行注册
     * @param userRegisterForm 用户注册表单
     * @return 用户注册id
     */
    @Override
    public String register(UserRegisterForm userRegisterForm) {
        User user = assembleUserEntity(userRegisterForm);
        try {
            if (!save(user)){
                throw new KunBusinessException("用户注册失败");
            }
        }catch (DuplicateKeyException e){
            throw new KunBusinessException("手机号已经存在");
        }
        return IdUtil.encrypt(user.getUserId());
    }

    /**
     * 用户登录
     * 1.校验验证码是否有效
     * 2.检查用户状态是否异常 如 是否禁用 是否临时管控
     * 3.执行登录
     * @param userPwdLoginForm 用户登录表单
     * @return 有时效性的token
     */
    @Override
    public SaTokenInfo pwdLogin(UserPwdLoginForm userPwdLoginForm) {
        checkCaptcha(userPwdLoginForm.getCaptchaKey(),userPwdLoginForm.getCaptchaCode());
        User user = checkUserStatus(userPwdLoginForm.getPhoneNumber(), userPwdLoginForm.getPassword());
        StpUtil.login(user.getUserId());
        SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
        return tokenInfo;
    }

    /**
     * 用户登出接口
     */
    @Override
    public void exit() {
        StpUtil.logout(StpUtil.getLoginIdAsLong());                    // 强制指定账号注销下线
    }

    /**
     * 校验用户状态 检查密码是否正确
     * @param phoneNumber 手机号
     * @param password 密码
     */
    private User checkUserStatus(String phoneNumber, String password) {
        User record = getOne(new LambdaQueryWrapper<User>().eq(User::getUserPhone, phoneNumber));
        if (record.getUserStatus()==1){
            throw new KunLoginException("用户状态异常");
        }
        if (StrUtil.isEmpty(record.getUserPwd())){
            throw new KunLoginException("您账户下未设置密码，请使用手机号进行登录");
        }
        String salt = record.getUserSalt();
        password = PasswordUtil.encryptPassword(salt, password);
        if (!Objects.equals(password, record.getUserPwd())) {
            throw new KunLoginException("密码不正确");
        }
        return record;
    }

    /**
     * 校验验证码是否正确
     * @param captchaKey 验证码key
     * @param captchaCode 用户填写验证码值
     */
    private void checkCaptcha(String captchaKey, String captchaCode) {
        String code =(String) redisTemplate.opsForValue().get(captchaKey);
        redisTemplate.delete(captchaKey);
        if (ObjectUtil.isNull(code) || !Objects.equals(code,captchaCode)) {
            throw new KunLoginException("验证码错误");
        }
    }



    /**
     * 处理注册用户实体
     * @param userRegisterForm 用户注册表单
     * @return 用户实体
     */
    private User assembleUserEntity(UserRegisterForm userRegisterForm) {
        User user = new User();
        user.setUserId(IdUtil.get());
        user.setUserName(userRegisterForm.getName());
        user.setUserPhone(userRegisterForm.getPhoneNumber());
        String salt = PasswordUtil.getSalt();
        user.setUserSalt(salt);
        user.setUserPwd(PasswordUtil.encryptPassword(salt, userRegisterForm.getPassword()));
        user.setCreateTime(LocalDateTime.now());
        user.setUpdateTime(LocalDateTime.now());
        return user;
    }
}
